package sk.tuke.constraintbuilder.model;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

import sk.tuke.constraintbuilder.model.ConstraintPatterns.ConstraintPattern;
import sk.tuke.constraintbuilder.utils.MyPreferences;
import sk.tuke.constraintbuilder.utils.XMLUtils;

public class Model {
	
	private static Model model;
	private List<Ruleset> rulesets = new ArrayList<>();
	private List<Constraint> constraints = new ArrayList<>();
	private List<Template> templates = new ArrayList<>();
	private HashMap<Integer, File> files = new HashMap<>();
	
	public Model(){
		createModel();
	}

	public static Model getModel(){
		if (model == null) {
			model = new Model();
		}
		return model;
	}
	
	public void createModel(){
		rulesets.clear();
		File dir = new File(MyPreferences.RULESETS_DIR);
		if(dir.isDirectory()){
			File[] files = dir.listFiles();
			for (File file : files) {
				Ruleset ruleset = XMLUtils.xmlToRuleset(file);
				if(ruleset!=null){
					rulesets.add(ruleset);
					this.files.put(ruleset.getId(), file);
				}
			}
		} 
		dir = new File(MyPreferences.CONSTRAINTS_DIR);
		if(dir.isDirectory()){
			File[] files = dir.listFiles();
			for (File file : files) {
				Constraint constraint = XMLUtils.xmlToConstraint(file);
				if(constraint!=null){
					constraints.add(constraint);
					this.files.put(constraint.getId(), file);
				}
			}
		}
		dir = new File(MyPreferences.TEMPLATES_DIR);
		if(dir.isDirectory()){
			File[] files = dir.listFiles();
			for (File file : files) {
				Template template = XMLUtils.xmlToTemplate(file);
				if(template!=null){
					templates.add(template);
					this.files.put(template.getId(), file);
				}
			}
		}
	}
	
	public void save(RootElement element){
		try {
			
			JAXBContext context = JAXBContext.newInstance(
					element instanceof Template ?   Template.class : (
				    element instanceof Constraint ? Constraint.class : 
				    	                            Ruleset.class));
			
			Marshaller marshaller = context.createMarshaller();
			marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
			
			String dir = element instanceof Template ?   MyPreferences.TEMPLATES_DIR :(
					     element instanceof Constraint ? MyPreferences.CONSTRAINTS_DIR : 
					    	                             MyPreferences.RULESETS_DIR);
			new File(dir).mkdirs();
			
			File file = files.get(element.getId());
			if(file==null){
				file = new File(dir+"/"+element.getName()+".xml");
				files.put(element.getId(), file);
			}

			OutputStream fos = new FileOutputStream(file);
			
			element = element instanceof Template ?   ((Template)element) : (
					  element instanceof Constraint ? ((Constraint)element) : 
					                                  ((Ruleset)element)) ;
			marshaller.marshal( element, fos );
			fos.close();
			
		} catch (JAXBException | IOException e) {
			e.printStackTrace();
		}
	}

	public List<Template> getTemplates() {
		return templates;
	}
	
	public List<Constraint> getConstraints(){
		return constraints;
	}
	
	public List<Ruleset> getRulesets(){
		return rulesets;
	}

	public Template getTemplateById(int id) {
		for (Template template : templates) {
			if (template.getId() == id) {
				return template;
			}
		}
		return null;
	}
	
	public Template getTemplateByName(String name) {
		for (Template template : templates) {
			if (template.getName().equals(name)) {
				return template;
			}
		}
		return null;
	}
	
	public void addTemplate(Template template){
		if(!templates.contains(template))
			templates.add(template);
	}
	
	public Constraint getConstraintById(int id) {
		for (Constraint constraint : constraints) {
			if (constraint.getId() == id) {
				return constraint;
			}
		}
		return null;
	}
	
	public Constraint getConstraintByName(String name) {
		for (Constraint constraint : constraints) {
			if (constraint.getName().equals(name)) {
				return constraint;
			}
		}
		return null;
	}
	
	public void addConstraint(Constraint constraint){
		if(!constraints.contains(constraint))
			constraints.add(constraint);
	}

	public Ruleset getRulesetById(int id) {
		for (Ruleset ruleset : rulesets) {
			if (ruleset.getId() == id) {
				return ruleset;
			}
		}
		return null;
	}
	
	public Ruleset getRulesetByName(String name) {
		for (Ruleset ruleset : rulesets) {
			if (ruleset.getName().equals(name)) {
				return ruleset;
			}
		}
		return null;
	}
	
	public void addRuleset(Ruleset ruleset){
		if(!rulesets.contains(ruleset))
			rulesets.add(ruleset);
	}
	
	public RootElement getElementById(int id){
		RootElement element = getConstraintById(id);
		if(element == null) element = getTemplateById(id);
		if(element == null) element = getRulesetById(id);
		return element;
	}
	
	public File getFile(Integer id){
		return files.get(id);
	}

	public void delete(RootElement element){
		File file = files.remove(element.getId());
		if(file!=null && file.exists())file.delete();
		if(templates.remove(element)==false)
			if(constraints.remove(element)==false)
				rulesets.remove(element);
	}

}
